#!/usr/bin/env ruby


## Credits                                                                                          ##
# -------------------------------------------------------------------------------------------------- #
# Created By: Michael Christenson II                                                                 #
#      Email: michael@theurbanrebellion.com                                                          #
#       Date: March 26, 2008                                                                         #
# -------------------------------------------------------------------------------------------------- #
## Notes                                                                                            ##
# If you have not created the back structure already, this script create it for you automatically.   #
# ** Make sure that you have permission to the backup directories root directory! **                 #
# -------------------------------------------------------------------------------------------------- #
## Example Usage                                                                                    ##
# -------------------------------------------------------------------------------------------------- #
# Command Line:                                                                                      #
# /usr/bin/env ruby /path/cronscript /path/myapplication.yml every10minutes                          #
# /usr/bin/env ruby /path/cronscript /path/myapplication.yml hourly                                  #
# /usr/bin/env ruby /path/cronscript /path/myapplication.yml daily                                   #
# /usr/bin/env ruby /path/cronscript /path/myapplication.yml weekly                                  #
# /usr/bin/env ruby /path/cronscript /path/myapplication.yml monthly                                 #
# /usr/bin/env ruby /path/cronscript /path/myapplication.yml yearly                                  #
# -------------------------------------------------------------------------------------------------- #
# Cron Jobs:                                                                                         #
# 0,10,20,30,40,50 * * * * /usr/bin/env ruby /path/cronscript /path/myapplication.yml every10minutes #
# 0 * * * * /usr/bin/env ruby /path/cronscript /path/myapplication.yml hourly                        #
# 55 22 * * * /usr/bin/env ruby /path/cronscript /path/myapplication.yml daily                       #
# 55 22 * * 6 /usr/bin/env ruby /path/cronscript /path/myapplication.yml weekly                      #
# 0 0 1 * * /usr/bin/env ruby /path/cronscript /path/myapplication.yml monthly                       #
# 59 23 1 1 * /usr/bin/env ruby /path/cronscript /path/myapplication.yml yearly                      #
# -------------------------------------------------------------------------------------------------- #


require 'rubygems'
require 'yaml'
require 'aws/s3'


## Start Variable Definitions ##

  @timestamp = `date +%Y%m%d-%H%MEST`.chomp
  
  @settings = YAML::load_file(ARGV[0])
  @mode = ARGV[1]
  
  @backup_path = @settings['backup']['path']
  @temp_path = "#{@backup_path}/tmp"
  @temp_backup_path = "#{@temp_path}/#{@timestamp}"
  @mode_path = "#{@backup_path}/#{@mode}"
  @application_path = @settings['application']['path']
  
  puts "[ ** Paths ** ]"
  puts "\tbackup_path => #{@backup_path}"
  puts "\ttemp_path => #{@temp_path}"
  puts "\ttemp_backup_path => #{@temp_backup_path}"
  puts "\tmode_path => #{@mode_path}"
  puts "\tapplication_path => #{@application_path}\n"
  
  @application_name = @settings['application']['name']
  @database = @settings['application']['@database']
  @database_user = @settings['application']['database_user']
  @database_password = @settings['application']['database_password']
  
  @bucket = @settings['s3']['bucket']
  AWS::S3::Base.establish_connection!(
    :access_key_id => @settings['s3']['access_key_id'],
    :secret_access_key => @settings['s3']['secret_access_key']
  )
  @s3drive = AWS::S3::Bucket.find(@bucket)

## End Variable Definitions ##

## Start Funtion Definitions ##

  def ensure_backup_structure
    find_or_create_directory(@backup_path)
    find_or_create_directory(@temp_path)
    find_or_create_directory(@mode_path)
  end
    
  def find_or_create_directory(directory)
    split_path = directory.split("/")
    split_path.each_with_index do |p,i|
    	full_path = []
    	(i+1).times do |j|
    		full_path.push(split_path[j]) if split_path[j] != nil
    	end
    	full_path = full_path.join("/")
    	if full_path.length > 1
    	  begin
    	    Dir.open(full_path)
    	    puts "\t** The path #{full_path} does exist. Exiting **"
        rescue
          print "\t** The path #{full_path} DOES NOT exist! Creating #{full_path} ... "
    	    Dir.mkdir(full_path)
    	    puts "done. Exiting **"
        end
      end
    end
  end
  
  def basic_backup
    ensure_backup_structure
    create_scaffold_structure
    dump_database
    dump_files
    finish_backup "every10minutes"
    destroy_scaffold_structure
  end
  
  def collect_for_hourly_backup
    ensure_backup_structure
    puts "mv #{@backup_path}/every10minutes #{@temp_backup_path}/every10minutes"
    puts "mkdir #{@backup_path}/every10minutes"
    
    system "mv #{@backup_path}/every10minutes #{@temp_backup_path}/every10minutes"
    system "mkdir #{@backup_path}/every10minutes"
    finish_backup "hourly", "every10minutes"
  end
  
  def collect_for_daily_backup
    ensure_backup_structure
    puts "mv #{@backup_path}/hourly #{@temp_backup_path}/hourly"
    puts "mkdir #{@backup_path}/hourly"
    
    system "mv #{@backup_path}/hourly #{@temp_backup_path}/hourly"
    system "mkdir #{@backup_path}/hourly"
    finish_backup "daily", "hourly"
  end
  
  def collect_for_weekly_backup
    ensure_backup_structure
    puts "mv #{@backup_path}/daily #{@temp_backup_path}/daily"
    puts "mkdir #{@backup_path}/daily"
    
    system "mv #{@backup_path}/daily #{@temp_backup_path}/daily"
    system "mkdir #{@backup_path}/daily"
    finish_backup "weekly", "daily"
  end
  
  def collect_for_monthly_backup
    ensure_backup_structure
    puts "mv #{@backup_path}/weekly #{@temp_backup_path}/weekly"
    puts "mkdir #{@backup_path}/weekly"
    
    system "mv #{@backup_path}/weekly #{@temp_backup_path}/weekly"
    system "mkdir #{@backup_path}/weekly"
    finish_backup "monthly", "weekly"
  end
  
  def collect_for_yearly_backup
    ensure_backup_structure
    puts "mv #{@backup_path}/monthly #{@temp_backup_path}/monthly"
    puts "mkdir #{@backup_path}/monthly"
    
    system "mv #{@backup_path}/monthly #{@temp_backup_path}/monthly"
    system "mkdir #{@backup_path}/monthly"
    finish_backup "yearly", "monthly"
  end
  
  def finish_backup(prefix, old_prefix = nil)
    compress_dumps
    clear_s3_prefix(old_prefix) if old_prefix
    backup_compressed_dump prefix
  end
  
  def create_scaffold_structure
    make_temporary_directories
    make_temporary_links
  end
  
  def make_temporary_directories
    puts "mkdir #{@temp_backup_path}"
    
    system "mkdir #{@temp_backup_path}"
  end
  
  def parse_file_path(file)
    path = file.split(/\//)
    file = path.pop
    path = path.join("/")
    
    return path,file
  end
  
  def make_temporary_links
    @settings['application']['files'].each do |counter, file|
      path, file = parse_file_path(file)
      
      
      puts "ln -s #{@application_path}/#{path} #{@temp_path}/linked_#{counter}"
      
      system "ln -s #{@application_path}/#{path} #{@temp_path}/linked_#{counter}"
    end
  end
  
  def dump_database
    if @database_password
      puts "mysqldump -u#{@database_user} -p#{@database_password} #{@database} > #{@temp_backup_path}/#{@application_name}_#{@timestamp}.sql"
      
      system "mysqldump -u#{@database_user} -p#{@database_password} #{@database} > #{@temp_backup_path}/#{@application_name}_#{@timestamp}.sql"
    else
      puts "mysqldump -u#{@database_user} #{@database} > #{@temp_backup_path}/#{@application_name}_#{@timestamp}.sql"
      
      system "mysqldump -u#{@database_user} #{@database} > #{@temp_backup_path}/#{@application_name}_#{@timestamp}.sql"
    end
  end
  
  def dump_files
    @settings['application']['files'].each do |counter, file|
      path, file = parse_file_path(file)
      puts "cd #{@temp_path}/linked_#{counter}/; find #{file} | xargs -i cp \{\} #{@temp_backup_path}/;"
      
      system "cd #{@temp_path}/linked_#{counter}/; find #{file} | xargs -i cp \{\} #{@temp_backup_path}/;"
    end
  end
  
  def compress_dumps
    puts "tar -zcf  #{@temp_path}/#{@application_name}_#{@timestamp}.tar.gz #{@temp_backup_path}"
    
    system "tar -zcf  #{@temp_path}/#{@application_name}_#{@timestamp}.tar.gz #{@temp_backup_path}"
  end
  
  def backup_compressed_dump(prefix)
    puts "mv #{@temp_path}/#{@application_name}_#{@timestamp}.tar.gz #{@mode_path}/"
    
    upload_dumps_to_s3 prefix, "#{@temp_path}/#{@application_name}_#{@timestamp}.tar.gz"
    system "mv #{@temp_path}/#{@application_name}_#{@timestamp}.tar.gz #{@mode_path}/"
  end
  
  def upload_dumps_to_s3(prefix, file)
    path, file_name = parse_file_path(file)
    puts "[ ** Storing #{file_name} ** ]"
    AWS::S3::S3Object.store("#{prefix}/#{file_name}", open(file), @s3drive.name)
    puts "[ ** done! ** ]"
  end
  
  def clear_s3_prefix(prefix)
    items = @s3drive.ojects(:prefix => "#{prefix}/")
    length = items.length
    puts "[ ** Clearing #{length} items from #{prefix} ** ]"
    print "["
    items.each do |file|
      AWS::S3::S3Object.delete(file.key, @s3drive.name)
      print "#"
    end
    puts "] done!"
  end
  
  def remove_temporary_links
    @settings['application']['files'].each do |counter, file|
      path, file = parse_file_path(file)
      
      puts "rm #{@temp_path}/linked_#{counter}"
      
      system "rm #{@temp_path}/linked_#{counter}"
    end
  end
  
  def remove_temporary_directories
    puts "rm -R #{@temp_backup_path}"
    
    system "rm -R #{@temp_backup_path}"
  end
  
  def destroy_scaffold_structure
    remove_temporary_links
    remove_temporary_directories
  end

## End Function Definitions ##

## Start Script ##

  puts "\n[ ** Starting #{@application_name} backup for #{@timestamp} ** ]"
  
  case @mode
    when "every10minutes"
      basic_backup
    when "hourly"
      collect_for_hourly_backup
    when "daily"
      collect_for_daily_backup
    when "weekly"
      collect_for_weekly_backup
    when "monthly"
      collect_for_monthly_backup
    when "yearly"
      collect_for_yearly_backup
    else
      basic_backup
  end
  
  puts "[ ** #{@application_name} #{@mode} Backup for #{@timestamp} is DONE ** ]\n"
  
## End Script ##